from django.shortcuts import render
from rest_framework_simplejwt.views import TokenObtainSlidingView # 原本的视图：目地改变源码里面的序列化类
from .serializer import TokenSerializer # 重写的序列化
from django.views import View # 视图
from uitl.verifyutil import CheckCode # 验证码类
import io # 文件字节流
from django.http import HttpResponse
from django_redis import get_redis_connection # 导入连接redis数据库的方法
from wen.redisconfile import LOGIN_KEY_TEMPLATE # 导入redis的键名
from wen.redisconfile import EXPIRE_TIME # 导入验证码的过期时间


from django.contrib.auth.models import User # 导入用户模型
from rest_framework.viewsets import GenericViewSet # 导入通用视图集
from rest_framework.mixins import RetrieveModelMixin # 导入单个接口的查询
from rest_framework.mixins import UpdateModelMixin # 导入修改接口
from rest_framework.response import Response # 返回给前端
from rest_framework.status import HTTP_404_NOT_FOUND,HTTP_400_BAD_REQUEST # 状态码

from .serializer import UserSerializer,UserParticularsSerializer # 第一个:用户序列化器,第二个:用户详情序列化器
from uitl.coustomPermissions import ValidUser # 个人权限
from rest_framework.decorators import action # 注册路由
from .serializer import UserPasswordSerializer # 用户的密码序列化器

# Create your views here.

class MyTokenView(TokenObtainSlidingView): # 继承原本的父类
    serializer_class = TokenSerializer # 指定的序列化器


class VerifyView(View):
    def get(self,request,uuid):
        '''
        获取图形验证码
        :param request:
        :param uuid: 前端传入uuid
        :return:
        '''

        img = CheckCode() # 实例化
        res,cone = img.img_code() # 调用生成验证码:第一个:返回画布,第二个:返回随机生成的验证码
        print(cone)

        # 图片以二进制形式进行传输
        node = io.BytesIO() # 创建一个字节流数据
        # 将图片以png格式保存到管道
        res.save(node,format='PNG') # format格式
        # 从字节流管道获取到二进制图片
        node_get = node.getbuffer()

        # 将随机生成的验证码(cone)存入redis数据库当中
        # 连接redis数据库
        conte = get_redis_connection(alias='verify_codes')  # 默认是自动找setting配置文件的(default)键名的库,通过alias= 指定使用哪个setting配置文件下的键名库

        # 把验证码保存到redis数据库中
        '''
        验证码的键名使用:(UUID具有唯一性),方便分辨哪个用户
        '''
                #  第一个:键名(注意要传入uudi)具有唯一性的                    第二个:值       第三个:验证码的有效时间
        conte.set(LOGIN_KEY_TEMPLATE % uuid,cone,EXPIRE_TIME)  # redis保存数据的格式是(键与值):key:value


        return HttpResponse(node_get,content_type='image/png')  # content_type指定文件的格式类型





class UserViewSet(RetrieveModelMixin,GenericViewSet):
    '''
    用户视图
    功能是查询个人的信息
    '''
    # 查询哪个模型 查询的是有效用户
    # 变量名固定的
    queryset = User.objects.filter(is_active=1)  # is_active= 用户模型的is_active字段表示:1:是有效用户 ,0:不是有效用户
    serializer_class = UserSerializer  # 指定序列化器
    permission_classes = [ValidUser] # 指定权限
    # 方法名固定的
    def update(self,request,pk):
        '''
        # 重写 UpdateModelMixin接口的，修改方法
        要同时修改用户模型与用户详情模型的信息更新
        1.修改User用户模型表的更新
            通过pk查询用户对象
            拿到前端传入的数据request.data,反序列化
            字段校验is_valid
            更新数据

        2.修改UserParticulars 用户详情模型表的更新
            判断用户是否有无详情
            有:获取详情
            无:创建详情
            字段校验
            更新数据
        :param request:
        :param pk:
        :return:
        '''
        #1.修改User用户模型表的更新
            #通过pk查询用户对象
        try:
            user = User.objects.get(pk=pk)  # get方法查询的时候没有该用户会报错,所以要异常处理一下
        except User.DoesNotExist:  # 异常处理
            return Response(status=HTTP_404_NOT_FOUND) # 返回一个状态码

            #拿到前端传入的数据request.data, 反序列化
        user_serializer = self.get_serializer(user,data=request.data) # 在类里面的序列化,要调用self.get_serializer

            #字段校验is_valid
        user_serializer.is_valid(raise_exception=True)

            #更新数据
        user_serializer.save()

        # 2.修改UserParticulars 用户详情模型表的更新
        #   用户详情模型表的更新
        user_particulars = request.data['userparticulars'] # 一对一字段的外键
        #   判断用户是否有无详情
        if hasattr(user,'userparticulars'):
            # 有: 获取详情
            user_particulars_serializer = UserParticularsSerializer(user.user,data=user_particulars)
        # 无: 创建详情
        else:
            user_particulars_serializer = UserParticularsSerializer(data=user_particulars)

        #  字段校验
        user_particulars_serializer.is_valid(raise_exception=True)
        #  更新数据
        user_particulars_serializer.save()
        # 构造返回的数据结构
                            # 键名是用户序列化器自定义字段的名称
        user_serializer.data['userparticulars'] = user_particulars_serializer.data # 详情
        return Response(user_serializer.data) # 返回


    # 重写指定序列化器的方法: 方法名固定的
    def get_serializer(self, *args, **kwargs):
        if self.action == 'Password':  # 判断是否是修改密码的接口
            return UserPasswordSerializer(*args,**kwargs) # 指定密码的序列化器
        return self.serializer_class(*args,**kwargs)


    # 修改密码
    @action(methods=['put'],detail=True) # 注册路由,methods请求方法,detail=True表示单个的
    def Password(self,request,pk):
        '''
        1.通过pk查到用户对象
        2.拿到传入的数据:request.data
        3.字段校验
        4.判断提交的旧密码和数据库里面存放的密码是否一致
                正确:修改
                错误:返回状态码,终止程序
        :param request:
        :param pk:
        :return:
        '''
        #   1.通过pk查到用户对象
        try:
            user = User.objects.get(pk=pk)  # 获取用户对象
        except User.DoesNotExist: # 没有该用户异常处理
            return Response(status=HTTP_404_NOT_FOUND) # 返回状态码
        #   2.拿到传入的数据: request.data
        password = request.data['password'] # 获取旧密码
        new_password = request.data['new_password'] # 获取新密码
        #   3.字段校验
        serializer = self.get_serializer(data=request.data) # 反向序列化
        serializer.is_valid(raise_exception=True)
        #   4.判断提交的旧密码和数据库里面存放的密码是否一致
        if user.check_password(password): # 校验提交的旧密码与存放在数据库的密码是否一致，是返回True,不是返回False
            #       正确: 修改
            user.set_password(new_password)
            user.save() # 保存数据
            # 返回提示信息:msg键名是固定的
            return Response({'msg':'修改成功'})

        #       错误: 返回状态码, 终止程序
        return Response(status=HTTP_400_BAD_REQUEST)






